/* Include files. */

extern "C" { 
	#include "standard.h"
	#include "util.h"
	#include "debug.h"
	int luaopen_subsystem(lua_State *L);
} 

#include "subsystemcore.h"
#include "message.h"
#include "network.h"
#include "netobject.h"
#include "cplusplus.h"
#include "timercore.h"
#include "event.h"

/* Local function declarations. */

/* Macro API definitions. */

/* Global variable declarations. */
extern struct ev_loop *main_loop;

/* Type definitions. */

/* Local function definitions. */

map<string, subSystem *> subSystem::sub_system_manager;
queue<subSystem *> subSystem::message_queue;

#ifdef U_LOOP		
extern struct uloop_timeout script_uloop_handle;
#endif

int sendSubSystem(lua_State *L, const char *from, 
		const char *to, const char *type, const char *msg, 
		u32 session) 
{
	subSystem *sub = subSystem::findSubSystem(to);
	if (sub) {
		message *m = message::create(from, to, session, type, msg);
		sub->_msg_queue.push(m);
		subSystem::message_queue.push(sub);
		
#ifdef U_LOOP
		if(!script_uloop_handle.pending)
			uloop_timeout_set(&script_uloop_handle, 0);
#endif

		return 0;
	}
	else{
		WARN("%s module isn't exist!\n", to);
		return 0;
	}
}

#if 0
int luaNewSubSystem(lua_State *L)
{
	lua_getfield(L, LUA_REGISTRYINDEX, "sub");
	subSystem *sub = (subSystem *)lua_touserdata(L, -1);
	if (sub == NULL) {
		return luaL_error(L, "subSystem error");
	}
	/*TODO: delete ûʵ��*/
	#if 0
	subSystem *sub = new subSystem(ctx->_name, ctx);
	ctx->_sub = sub;
	if(luaL_newmetatable(L, "context")){
		luaL_Reg l[]={
			{"xxx", xxx},
			{NULL, NULL},
		};
		luaL_newlib(L, l);
		lua_setfield(L, -2, "__index");
		//lua_pushcfunction(L, lrelease);
		//lua_setfield(L, -2, "__gc");
	}
	lua_setmetatable(L, -2);
	#endif
	subSystem::createSubSystem(sub);
	return 1;
}
#endif

void subSystem::createSubSystem(subSystem *sub) 
{
	sub_system_manager[sub->_name] = sub;	
}

void subSystem::deleteSubSystem(subSystem *sub) 
{
	sub_system_manager.erase(sub->_name);	
}

subSystem* subSystem::findSubSystem(string key) 
{
	map<string, subSystem *>::iterator ite = sub_system_manager.find(key);  
	if(ite == sub_system_manager.end()){  
		return NULL;  
	}
	else{  
		return ite->second;  
	}  
}

bool subSystem::bindSubSystem(string name, netObject *netobj) 
{
	subSystem *sub = subSystem::findSubSystem(name);
	if (sub) {
		sub->addNetObject(netobj);
		return true;
	}
	else {
		return false;
	}
}

subSystem::subSystem(string name, lua_State *L)
{
	_name = name;
	_L = L;
	_cost_mem = 0;
	_cost_cpu = 0;
	_net_object_id = 0;
	subSystem::createSubSystem(this);
}

subSystem::~subSystem()
{
	subSystem::deleteSubSystem(this);	
	while(messageSize() > 0) {
		message *m = _msg_queue.front();
		_msg_queue.pop();
		message::destroy(m);
	}

	map<u32, timeObj*>::iterator iter_t;
	for(iter_t = _time_obj_map.begin(); iter_t != _time_obj_map.end(); iter_t++) {
		timeObj *tobj = iter_t->second;
#ifdef U_LOOP
		uloop_timeout_cancel(&tobj->_w);
#else
		ev_timer_stop(main_loop, &tobj->_w);
#endif	
		delete tobj;
	}  
	_time_obj_map.clear();

	set<event*>::iterator iter_e;
	for(iter_e = _event_set.begin(); iter_e != _event_set.end(); iter_e++) {
		event *eobj = *iter_e;		
		eobj->_sub = NULL;
		eobj->_L = NULL;
	} 
	_event_set.clear();

	map<u32, netObject *>::iterator iter_n;
	for(iter_n = _net_object_map.begin(); iter_n != _net_object_map.end(); iter_n++) {
		netObject *nobj = iter_n->second;
		delete nobj;
	}  
	_net_object_map.clear();

	lua_settop(_L, 0);
	lua_close(_L);
	INFO("subsystem %s delete success!\n", _name.c_str());
}

timeObj* subSystem::findTimeObj(u32 key)
{
	map<u32, timeObj* >::iterator ite = _time_obj_map.find(key);  
	if(ite == _time_obj_map.end()){ 
		return NULL;  
		
	}
	else{  
		return ite->second;  
	}  
}

void subSystem::removeTimeObj(u32 key)
{
	map<u32, timeObj* >::iterator ite = _time_obj_map.find(key);  
	if(ite == _time_obj_map.end()){  
		ASSERT(0);
	}
	else{  
		_time_obj_map.erase(key);
	}  
	return;
}

netObject* subSystem::findNetObject(u32 key) 
{
	map<u32, netObject *>::iterator ite = _net_object_map.find(key);  
	if(ite == _net_object_map.end()){
		DEBUG("netobject id = %u isn't exist!\n", key);
		return NULL;  
	}
	else{  
		return ite->second;  
	}  
}

void subSystem::addNetObject(netObject* netobj) 
{
	netobj->_sub = this;
	netobj->_id = ++_net_object_id;
	_net_object_count++;
	_net_object_map[netobj->_id] = netobj;
}

void subSystem::removeNetObject(netObject* netobj) 
{
	_net_object_count--;
	if (netobj->_id)
		_net_object_map.erase(netobj->_id);	
}

void subSystem::addEvent(event *ev) 
{
	_event_set.insert(ev);
}

void subSystem::removeEvent(event *ev) 
{
	set<event *>::iterator ite = _event_set.find(ev);  
	if(ite == _event_set.end()){  
		WARN("the subsystem has exited! ev = %p\n", ev);
	}
	else{  
		_event_set.erase(ite);
	}  	
}

int subSystem::sendNetObject(lua_State *L) 
{
	const char *msg = luaL_checkstring(L, 1);
	u32 id = luaL_checkinteger(L, 2);
	netObject* netobj = findNetObject(id);
	if (netobj != NULL) {
		netobj->pushMsg(msg);
	}
	return 0;
}

int subSystem::sendSubSystem(lua_State *L) 
{
	const char *to = luaL_checkstring(L, 1);
	const char *type = luaL_checkstring(L, 2);
	const char *msg = luaL_checkstring(L, 3);
	u32 session = luaL_checkinteger(L, 4);
	
	return this->sendSubSystem(L, to, type, msg, session);
}

int subSystem::sendSubSystem(lua_State *L, 
				const char *to, const char *type, 
				const char *msg, u32 session) 
{
	subSystem *sub = subSystem::findSubSystem(to);
	if (sub) {
		message *m = message::create(_name.c_str(), to, session, type, msg);
		sub->_msg_queue.push(m);
		subSystem::message_queue.push(sub);
		
#ifdef U_LOOP
		if(!script_uloop_handle.pending)
			uloop_timeout_set(&script_uloop_handle, 0);
#endif

		return 0;
	}
	else{
		DEBUG("%s module isn't exist\n", to);
		return 0;
	}
}

